library(tidyverse)
── Attaching core tidyverse packages ──────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.4     ── Conflicts ────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(WGCNA)
Loading required package: dynamicTreeCut
Loading required package: fastcluster

Attaching package: ‘fastcluster’

The following object is masked from ‘package:stats’:

    hclust


Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
Registered S3 method overwritten by 'rmarkdown':
  method         from
  print.paged_df     
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     

Attaching package: ‘WGCNA’

The following object is masked from ‘package:stats’:

    cor
library(gplots)

Attaching package: ‘gplots’

The following object is masked from ‘package:stats’:

    lowess
options(stringsAsFactors = FALSE)

load sample info

sample.description <- read.csv("../input/sample.description.csv")

load reads

lcpm <- read.csv("../output/log2cpm.csv.gz", row.names = 1, check.names = FALSE)
head(lcpm)
dim(lcpm)
[1] 26704    48

Filter for sporophyte samples:

sample.description <- sample.description %>% filter(str_detect(tissue, "S5|WYS", negate = TRUE))
lcpm <- lcpm[,sample.description$sample]

Filter for genes with the highest coefficient of variation

CV <- apply(lcpm, 1, \(x) abs(sd(x)/mean(x)))
hist(log10(CV))

names(CV) <- rownames(lcpm)
CV[str_detect(names(CV), "18G076300|33G031700")]
Ceric.18G076300.v2.1 Ceric.33G031700.v2.1 
           0.1057739            0.2595446 
quantile(CV, 0.30)
      30% 
0.1033241 
lcpm.filter <- lcpm[CV > quantile(CV, 0.3),]
dim(lcpm.filter)
[1] 18693    18

WGCNA wants genes in columns

lcpm.filter.t <- t(lcpm.filter)

Soft thresholding

powers <- c(c(1:10), seq(from = 12, to=20, by=2))
sft <- pickSoftThreshold(lcpm.filter.t, powerVector = powers, verbose = 5,networkType = "signed hybrid", blockSize = 20000)
 pickSoftThreshold: calculating connectivity for given powers...
   ..working on genes 1 through 18693 of 18693
Warning: executing %dopar% sequentially: no parallel backend registered
sizeGrWindow(9, 5)
par(mfrow = c(1,2))
cex1 <- 0.9
# Scale-free topology fit index as a function of the soft-thresholding power
plot(sft$fitIndices[,1], -sign(sft$fitIndices[,3])*sft$fitIndices[,2],
     xlab="Soft Threshold (power)",ylab="Scale Free Topology Model Fit,signed R^2",type="n",
     main = paste("Scale independence"))
text(sft$fitIndices[,1], -sign(sft$fitIndices[,3])*sft$fitIndices[,2],
     labels=powers,cex=cex1,col="red")
# this line corresponds to using an R^2 cut-off of h
abline(h=0.90,col="red")
# Mean connectivity as a function of the soft-thresholding power
plot(sft$fitIndices[,1], sft$fitIndices[,5],
     xlab="Soft Threshold (power)",ylab="Mean Connectivity", type="n",
     main = paste("Mean connectivity"))
text(sft$fitIndices[,1], sft$fitIndices[,5], labels=powers, cex=cex1,col="red")

choose 5

softPower <- 5
adjacency <- adjacency(lcpm.filter.t, power = softPower, type = "signed hybrid")
# Turn adjacency into topological overlap
TOM <- TOMsimilarity(adjacency, TOMType = "signed");
..connectivity..
..matrix multiplication (system BLAS)..
..normalization..
..done.
dissTOM <- 1-TOM
# Call the hierarchical clustering function
geneTree <- hclust(as.dist(dissTOM), method = "average")
# Plot the resulting clustering tree (dendrogram)
sizeGrWindow(12,9)
plot(geneTree, xlab="", sub="", main = "Gene clustering on TOM-based dissimilarity",
     labels = FALSE, hang = 0.04)

define modules

# We like large modules, so we set the minimum module size relatively high:
minModuleSize <- 30;
# Module identification using dynamic tree cut:
dynamicMods <- cutreeDynamic(dendro = geneTree, distM = dissTOM,
                             deepSplit <- 2, pamRespectsDendro = FALSE,
                             minClusterSize = minModuleSize);
 ..done.
table(dynamicMods)
dynamicMods
   1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19   20 
4959 2615 1863 1524  998  872  780  701  583  555  548  462  461  458  322  262  248  229  130  123 
# Convert numeric labels into colors
dynamicColors = labels2colors(dynamicMods)
table(dynamicColors)
dynamicColors
       black         blue        brown         cyan        green  greenyellow       grey60 
         780         2615         1863          458          998          548          248 
   lightcyan   lightgreen  lightyellow      magenta midnightblue         pink       purple 
         262          229          130          583          322          701          555 
         red    royalblue       salmon          tan    turquoise       yellow 
         872          123          461          462         4959         1524 
# Plot the dendrogram and colors underneath
sizeGrWindow(8,6)
plotDendroAndColors(geneTree, dynamicColors, "Dynamic Tree Cut",
                    dendroLabels = FALSE, hang = 0.03,
                    addGuide = TRUE, guideHang = 0.05,
                    main = "Gene dendrogram and module colors")

merge similar modules

# Calculate eigengenes
MEList <- moduleEigengenes(lcpm.filter.t, colors = dynamicColors)
MEs <- MEList$eigengenes
# Calculate dissimilarity of module eigengenes
MEDiss <- 1-cor(MEs);
# Cluster module eigengenes
METree <- hclust(as.dist(MEDiss), method = "average");
# Plot the result
sizeGrWindow(7, 6)
plot(METree, main = "Clustering of module eigengenes",
     xlab = "", sub = "")

merge with correlation > 0.75

MEDissThres = 0.25
# Plot the cut line into the dendrogram
plot(METree, main = "Clustering of module eigengenes",
     xlab = "", sub = "")
abline(h=MEDissThres, col = "red")

# Call an automatic merging function
merge = mergeCloseModules(lcpm.filter.t, dynamicColors, cutHeight = MEDissThres, verbose = 3)
 mergeCloseModules: Merging modules whose distance is less than 0.25
   multiSetMEs: Calculating module MEs.
     Working on set 1 ...
     moduleEigengenes: Calculating 20 module eigengenes in given set.
   multiSetMEs: Calculating module MEs.
     Working on set 1 ...
     moduleEigengenes: Calculating 14 module eigengenes in given set.
   multiSetMEs: Calculating module MEs.
     Working on set 1 ...
     moduleEigengenes: Calculating 13 module eigengenes in given set.
   Calculating new MEs...
   multiSetMEs: Calculating module MEs.
     Working on set 1 ...
     moduleEigengenes: Calculating 13 module eigengenes in given set.
# The merged module colors
mergedColors = merge$colors
# Eigengenes of the new merged modules:
mergedMEs = merge$newMEs

compare pre and post merge

sizeGrWindow(12, 9)
#pdf(file = "Plots/geneDendro-3.pdf", wi = 9, he = 6)
plotDendroAndColors(geneTree, cbind(dynamicColors, mergedColors),
                    c("Dynamic Tree Cut", "Merged dynamic"),
                    dendroLabels = FALSE, hang = 0.03,
                    addGuide = TRUE, guideHang = 0.05)

#dev.off()
# Rename to moduleColors
moduleColors = mergedColors
# Construct numerical labels corresponding to the colors
colorOrder = c("grey", standardColors(50));
moduleLabels = match(moduleColors, colorOrder)-1;
MEs = mergedMEs 
table(merge$colors)

       black         blue        brown         cyan        green       grey60    lightcyan 
         780         3198         3745          458          998          949          262 
  lightgreen  lightyellow midnightblue       purple       salmon    turquoise 
         229          253          322          555          461         6483 
length(table(merge$colors))
[1] 13
median(table(merge$colors))
[1] 555

Look at modules

Which module is LFY in?

CrLFY1 <- "Ceric.33G031700.2.v2.1" # this is the primary transcript.  There is another but it is expressed at very low levels.

CrLFY2 <- "Ceric.18G076300"

module.assignment <- tibble(geneID=colnames(lcpm.filter.t), module = mergedColors)

module.assignment %>%
  filter(str_detect(geneID, "18G076300|33G031700"))

Interesting: they are in different modules(!). But these are both very large modules

module.assignment %>% group_by(module) %>% summarize(n_genes = n()) %>% arrange(n_genes)

Plot eigengenes

Make sure sample info sheet is in the correct order.

rownames(lcpm.filter.t) %>% str_replace_all("\\.", "-") == sample.description$sample
 [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
sample.eigen <- cbind(sample.description, MEs)
sample.eigen
sample.eigen.l <- sample.eigen %>%
  mutate(gt_tissue=str_c(base_gt, "-", tissue)) %>%
  pivot_longer(starts_with("ME"), names_to = "ME")

sample.eigen.means <- sample.eigen.l %>%
  group_by(gt_tissue, ME) %>%
  summarise(value = mean(value))
`summarise()` has grouped output by 'gt_tissue'. You can override using the `.groups` argument.
sample.eigen.l %>%
  ggplot(aes(x=gt_tissue, y = value)) +
    geom_point(aes(color = tissue)) +
    geom_line(data=sample.eigen.means, group = 1, lwd=.3) + 
  facet_wrap(~ME, ncol=4) +
  theme(axis.text.x = element_text(angle = 90, hjust=1, vjust=.5)) +
  scale_color_brewer(type="qual", palette = "Set3")

A heat map:

MEs.m <- as.matrix(MEs)
heatmap.2(MEs.m, trace="none", cexRow= 0.6, col="bluered")

save(module.assignment, MEs, lcpm.filter, CrLFY1, CrLFY2, file="../output/WGCNA_gametophyteSamples.Rdata")
LS0tCnRpdGxlOiAiMDRfV0dDTkEiCmF1dGhvcjogIkp1bGluIE1hbG9vZiIKZGF0ZTogIjIwMjUtMDItMTYiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KFdHQ05BKQpsaWJyYXJ5KGdwbG90cykKb3B0aW9ucyhzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmBgYAoKbG9hZCBzYW1wbGUgaW5mbwpgYGB7cn0Kc2FtcGxlLmRlc2NyaXB0aW9uIDwtIHJlYWQuY3N2KCIuLi9pbnB1dC9zYW1wbGUuZGVzY3JpcHRpb24uY3N2IikKYGBgCgoKbG9hZCByZWFkcwoKYGBge3J9CmxjcG0gPC0gcmVhZC5jc3YoIi4uL291dHB1dC9sb2cyY3BtLmNzdi5neiIsIHJvdy5uYW1lcyA9IDEsIGNoZWNrLm5hbWVzID0gRkFMU0UpCmhlYWQobGNwbSkKZGltKGxjcG0pCmBgYAoKRmlsdGVyIGZvciBzcG9yb3BoeXRlIHNhbXBsZXM6CgpgYGB7cn0Kc2FtcGxlLmRlc2NyaXB0aW9uIDwtIHNhbXBsZS5kZXNjcmlwdGlvbiAlPiUgZmlsdGVyKHN0cl9kZXRlY3QodGlzc3VlLCAiUzV8V1lTIiwgbmVnYXRlID0gVFJVRSkpCmxjcG0gPC0gbGNwbVssc2FtcGxlLmRlc2NyaXB0aW9uJHNhbXBsZV0KYGBgCgoKRmlsdGVyIGZvciBnZW5lcyB3aXRoIHRoZSBoaWdoZXN0IGNvZWZmaWNpZW50IG9mIHZhcmlhdGlvbgoKYGBge3J9CkNWIDwtIGFwcGx5KGxjcG0sIDEsIFwoeCkgYWJzKHNkKHgpL21lYW4oeCkpKQpoaXN0KGxvZzEwKENWKSkKYGBgCmBgYHtyfQpuYW1lcyhDVikgPC0gcm93bmFtZXMobGNwbSkKQ1Zbc3RyX2RldGVjdChuYW1lcyhDViksICIxOEcwNzYzMDB8MzNHMDMxNzAwIildCmBgYAoKYGBge3J9CnF1YW50aWxlKENWLCAwLjMwKQpgYGAKCmBgYHtyfQpsY3BtLmZpbHRlciA8LSBsY3BtW0NWID4gcXVhbnRpbGUoQ1YsIDAuMyksXQpkaW0obGNwbS5maWx0ZXIpCmBgYAoKV0dDTkEgd2FudHMgZ2VuZXMgaW4gY29sdW1ucwoKYGBge3J9CmxjcG0uZmlsdGVyLnQgPC0gdChsY3BtLmZpbHRlcikKYGBgCgoKU29mdCB0aHJlc2hvbGRpbmcKYGBge3J9CnBvd2VycyA8LSBjKGMoMToxMCksIHNlcShmcm9tID0gMTIsIHRvPTIwLCBieT0yKSkKc2Z0IDwtIHBpY2tTb2Z0VGhyZXNob2xkKGxjcG0uZmlsdGVyLnQsIHBvd2VyVmVjdG9yID0gcG93ZXJzLCB2ZXJib3NlID0gNSxuZXR3b3JrVHlwZSA9ICJzaWduZWQgaHlicmlkIiwgYmxvY2tTaXplID0gMjAwMDApCmBgYAoKYGBge3J9CnNpemVHcldpbmRvdyg5LCA1KQpwYXIobWZyb3cgPSBjKDEsMikpCmNleDEgPC0gMC45CiMgU2NhbGUtZnJlZSB0b3BvbG9neSBmaXQgaW5kZXggYXMgYSBmdW5jdGlvbiBvZiB0aGUgc29mdC10aHJlc2hvbGRpbmcgcG93ZXIKcGxvdChzZnQkZml0SW5kaWNlc1ssMV0sIC1zaWduKHNmdCRmaXRJbmRpY2VzWywzXSkqc2Z0JGZpdEluZGljZXNbLDJdLAogICAgIHhsYWI9IlNvZnQgVGhyZXNob2xkIChwb3dlcikiLHlsYWI9IlNjYWxlIEZyZWUgVG9wb2xvZ3kgTW9kZWwgRml0LHNpZ25lZCBSXjIiLHR5cGU9Im4iLAogICAgIG1haW4gPSBwYXN0ZSgiU2NhbGUgaW5kZXBlbmRlbmNlIikpCnRleHQoc2Z0JGZpdEluZGljZXNbLDFdLCAtc2lnbihzZnQkZml0SW5kaWNlc1ssM10pKnNmdCRmaXRJbmRpY2VzWywyXSwKICAgICBsYWJlbHM9cG93ZXJzLGNleD1jZXgxLGNvbD0icmVkIikKIyB0aGlzIGxpbmUgY29ycmVzcG9uZHMgdG8gdXNpbmcgYW4gUl4yIGN1dC1vZmYgb2YgaAphYmxpbmUoaD0wLjkwLGNvbD0icmVkIikKIyBNZWFuIGNvbm5lY3Rpdml0eSBhcyBhIGZ1bmN0aW9uIG9mIHRoZSBzb2Z0LXRocmVzaG9sZGluZyBwb3dlcgpwbG90KHNmdCRmaXRJbmRpY2VzWywxXSwgc2Z0JGZpdEluZGljZXNbLDVdLAogICAgIHhsYWI9IlNvZnQgVGhyZXNob2xkIChwb3dlcikiLHlsYWI9Ik1lYW4gQ29ubmVjdGl2aXR5IiwgdHlwZT0ibiIsCiAgICAgbWFpbiA9IHBhc3RlKCJNZWFuIGNvbm5lY3Rpdml0eSIpKQp0ZXh0KHNmdCRmaXRJbmRpY2VzWywxXSwgc2Z0JGZpdEluZGljZXNbLDVdLCBsYWJlbHM9cG93ZXJzLCBjZXg9Y2V4MSxjb2w9InJlZCIpCmBgYApjaG9vc2UgNQoKYGBge3J9CnNvZnRQb3dlciA8LSA1CmFkamFjZW5jeSA8LSBhZGphY2VuY3kobGNwbS5maWx0ZXIudCwgcG93ZXIgPSBzb2Z0UG93ZXIsIHR5cGUgPSAic2lnbmVkIGh5YnJpZCIpCiMgVHVybiBhZGphY2VuY3kgaW50byB0b3BvbG9naWNhbCBvdmVybGFwClRPTSA8LSBUT01zaW1pbGFyaXR5KGFkamFjZW5jeSwgVE9NVHlwZSA9ICJzaWduZWQiKTsKZGlzc1RPTSA8LSAxLVRPTQpgYGAKCmBgYHtyfQojIENhbGwgdGhlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIGZ1bmN0aW9uCmdlbmVUcmVlIDwtIGhjbHVzdChhcy5kaXN0KGRpc3NUT00pLCBtZXRob2QgPSAiYXZlcmFnZSIpCiMgUGxvdCB0aGUgcmVzdWx0aW5nIGNsdXN0ZXJpbmcgdHJlZSAoZGVuZHJvZ3JhbSkKc2l6ZUdyV2luZG93KDEyLDkpCnBsb3QoZ2VuZVRyZWUsIHhsYWI9IiIsIHN1Yj0iIiwgbWFpbiA9ICJHZW5lIGNsdXN0ZXJpbmcgb24gVE9NLWJhc2VkIGRpc3NpbWlsYXJpdHkiLAogICAgIGxhYmVscyA9IEZBTFNFLCBoYW5nID0gMC4wNCkKYGBgCgpkZWZpbmUgbW9kdWxlcwoKYGBge3J9CiMgV2UgbGlrZSBsYXJnZSBtb2R1bGVzLCBzbyB3ZSBzZXQgdGhlIG1pbmltdW0gbW9kdWxlIHNpemUgcmVsYXRpdmVseSBoaWdoOgptaW5Nb2R1bGVTaXplIDwtIDMwOwojIE1vZHVsZSBpZGVudGlmaWNhdGlvbiB1c2luZyBkeW5hbWljIHRyZWUgY3V0OgpkeW5hbWljTW9kcyA8LSBjdXRyZWVEeW5hbWljKGRlbmRybyA9IGdlbmVUcmVlLCBkaXN0TSA9IGRpc3NUT00sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVlcFNwbGl0IDwtIDIsIHBhbVJlc3BlY3RzRGVuZHJvID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluQ2x1c3RlclNpemUgPSBtaW5Nb2R1bGVTaXplKTsKdGFibGUoZHluYW1pY01vZHMpCmBgYAoKYGBge3J9CiMgQ29udmVydCBudW1lcmljIGxhYmVscyBpbnRvIGNvbG9ycwpkeW5hbWljQ29sb3JzID0gbGFiZWxzMmNvbG9ycyhkeW5hbWljTW9kcykKdGFibGUoZHluYW1pY0NvbG9ycykKIyBQbG90IHRoZSBkZW5kcm9ncmFtIGFuZCBjb2xvcnMgdW5kZXJuZWF0aApzaXplR3JXaW5kb3coOCw2KQpwbG90RGVuZHJvQW5kQ29sb3JzKGdlbmVUcmVlLCBkeW5hbWljQ29sb3JzLCAiRHluYW1pYyBUcmVlIEN1dCIsCiAgICAgICAgICAgICAgICAgICAgZGVuZHJvTGFiZWxzID0gRkFMU0UsIGhhbmcgPSAwLjAzLAogICAgICAgICAgICAgICAgICAgIGFkZEd1aWRlID0gVFJVRSwgZ3VpZGVIYW5nID0gMC4wNSwKICAgICAgICAgICAgICAgICAgICBtYWluID0gIkdlbmUgZGVuZHJvZ3JhbSBhbmQgbW9kdWxlIGNvbG9ycyIpCmBgYAoKbWVyZ2Ugc2ltaWxhciBtb2R1bGVzCgpgYGB7cn0KIyBDYWxjdWxhdGUgZWlnZW5nZW5lcwpNRUxpc3QgPC0gbW9kdWxlRWlnZW5nZW5lcyhsY3BtLmZpbHRlci50LCBjb2xvcnMgPSBkeW5hbWljQ29sb3JzKQpNRXMgPC0gTUVMaXN0JGVpZ2VuZ2VuZXMKIyBDYWxjdWxhdGUgZGlzc2ltaWxhcml0eSBvZiBtb2R1bGUgZWlnZW5nZW5lcwpNRURpc3MgPC0gMS1jb3IoTUVzKTsKIyBDbHVzdGVyIG1vZHVsZSBlaWdlbmdlbmVzCk1FVHJlZSA8LSBoY2x1c3QoYXMuZGlzdChNRURpc3MpLCBtZXRob2QgPSAiYXZlcmFnZSIpOwojIFBsb3QgdGhlIHJlc3VsdApzaXplR3JXaW5kb3coNywgNikKcGxvdChNRVRyZWUsIG1haW4gPSAiQ2x1c3RlcmluZyBvZiBtb2R1bGUgZWlnZW5nZW5lcyIsCiAgICAgeGxhYiA9ICIiLCBzdWIgPSAiIikKYGBgCgptZXJnZSB3aXRoIGNvcnJlbGF0aW9uID4gMC43NQpgYGB7cn0KTUVEaXNzVGhyZXMgPSAwLjI1CiMgUGxvdCB0aGUgY3V0IGxpbmUgaW50byB0aGUgZGVuZHJvZ3JhbQpwbG90KE1FVHJlZSwgbWFpbiA9ICJDbHVzdGVyaW5nIG9mIG1vZHVsZSBlaWdlbmdlbmVzIiwKICAgICB4bGFiID0gIiIsIHN1YiA9ICIiKQphYmxpbmUoaD1NRURpc3NUaHJlcywgY29sID0gInJlZCIpCiMgQ2FsbCBhbiBhdXRvbWF0aWMgbWVyZ2luZyBmdW5jdGlvbgptZXJnZSA9IG1lcmdlQ2xvc2VNb2R1bGVzKGxjcG0uZmlsdGVyLnQsIGR5bmFtaWNDb2xvcnMsIGN1dEhlaWdodCA9IE1FRGlzc1RocmVzLCB2ZXJib3NlID0gMykKIyBUaGUgbWVyZ2VkIG1vZHVsZSBjb2xvcnMKbWVyZ2VkQ29sb3JzID0gbWVyZ2UkY29sb3JzCiMgRWlnZW5nZW5lcyBvZiB0aGUgbmV3IG1lcmdlZCBtb2R1bGVzOgptZXJnZWRNRXMgPSBtZXJnZSRuZXdNRXMKYGBgCgpjb21wYXJlIHByZSBhbmQgcG9zdCBtZXJnZQpgYGB7cn0Kc2l6ZUdyV2luZG93KDEyLCA5KQojcGRmKGZpbGUgPSAiUGxvdHMvZ2VuZURlbmRyby0zLnBkZiIsIHdpID0gOSwgaGUgPSA2KQpwbG90RGVuZHJvQW5kQ29sb3JzKGdlbmVUcmVlLCBjYmluZChkeW5hbWljQ29sb3JzLCBtZXJnZWRDb2xvcnMpLAogICAgICAgICAgICAgICAgICAgIGMoIkR5bmFtaWMgVHJlZSBDdXQiLCAiTWVyZ2VkIGR5bmFtaWMiKSwKICAgICAgICAgICAgICAgICAgICBkZW5kcm9MYWJlbHMgPSBGQUxTRSwgaGFuZyA9IDAuMDMsCiAgICAgICAgICAgICAgICAgICAgYWRkR3VpZGUgPSBUUlVFLCBndWlkZUhhbmcgPSAwLjA1KQojZGV2Lm9mZigpCmBgYAoKYGBge3J9CiMgUmVuYW1lIHRvIG1vZHVsZUNvbG9ycwptb2R1bGVDb2xvcnMgPSBtZXJnZWRDb2xvcnMKIyBDb25zdHJ1Y3QgbnVtZXJpY2FsIGxhYmVscyBjb3JyZXNwb25kaW5nIHRvIHRoZSBjb2xvcnMKY29sb3JPcmRlciA9IGMoImdyZXkiLCBzdGFuZGFyZENvbG9ycyg1MCkpOwptb2R1bGVMYWJlbHMgPSBtYXRjaChtb2R1bGVDb2xvcnMsIGNvbG9yT3JkZXIpLTE7Ck1FcyA9IG1lcmdlZE1FcyAKdGFibGUobWVyZ2UkY29sb3JzKQpsZW5ndGgodGFibGUobWVyZ2UkY29sb3JzKSkKbWVkaWFuKHRhYmxlKG1lcmdlJGNvbG9ycykpCgpgYGAKCiMjIExvb2sgYXQgbW9kdWxlcwoKV2hpY2ggbW9kdWxlIGlzIExGWSBpbj8KCmBgYHtyfQpDckxGWTEgPC0gIkNlcmljLjMzRzAzMTcwMCIKCkNyTEZZMiA8LSAiQ2VyaWMuMThHMDc2MzAwIgoKbW9kdWxlLmFzc2lnbm1lbnQgPC0gdGliYmxlKGdlbmVJRD1jb2xuYW1lcyhsY3BtLmZpbHRlci50KSwgbW9kdWxlID0gbWVyZ2VkQ29sb3JzKQoKbW9kdWxlLmFzc2lnbm1lbnQgJT4lCiAgZmlsdGVyKHN0cl9kZXRlY3QoZ2VuZUlELCAiMThHMDc2MzAwfDMzRzAzMTcwMCIpKQpgYGAKSW50ZXJlc3Rpbmc6IHRoZXkgYXJlIGluIGRpZmZlcmVudCBtb2R1bGVzKCEpLiAgQnV0IHRoZXNlIGFyZSBib3RoIHZlcnkgbGFyZ2UgbW9kdWxlcwoKYGBge3J9Cm1vZHVsZS5hc3NpZ25tZW50ICU+JSBncm91cF9ieShtb2R1bGUpICU+JSBzdW1tYXJpemUobl9nZW5lcyA9IG4oKSkgJT4lIGFycmFuZ2Uobl9nZW5lcykKYGBgCgpQbG90IGVpZ2VuZ2VuZXMKCk1ha2Ugc3VyZSBzYW1wbGUgaW5mbyBzaGVldCBpcyBpbiB0aGUgY29ycmVjdCBvcmRlci4KYGBge3J9CnJvd25hbWVzKGxjcG0uZmlsdGVyLnQpICU+JSBzdHJfcmVwbGFjZV9hbGwoIlxcLiIsICItIikgPT0gc2FtcGxlLmRlc2NyaXB0aW9uJHNhbXBsZQpgYGAKCmBgYHtyfQpzYW1wbGUuZWlnZW4gPC0gY2JpbmQoc2FtcGxlLmRlc2NyaXB0aW9uLCBNRXMpCnNhbXBsZS5laWdlbgpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTZ9CnNhbXBsZS5laWdlbi5sIDwtIHNhbXBsZS5laWdlbiAlPiUKICBtdXRhdGUoZ3RfdGlzc3VlPXN0cl9jKGJhc2VfZ3QsICItIiwgdGlzc3VlKSkgJT4lCiAgcGl2b3RfbG9uZ2VyKHN0YXJ0c193aXRoKCJNRSIpLCBuYW1lc190byA9ICJNRSIpCgpzYW1wbGUuZWlnZW4ubWVhbnMgPC0gc2FtcGxlLmVpZ2VuLmwgJT4lCiAgZ3JvdXBfYnkoZ3RfdGlzc3VlLCBNRSkgJT4lCiAgc3VtbWFyaXNlKHZhbHVlID0gbWVhbih2YWx1ZSkpCgpzYW1wbGUuZWlnZW4ubCAlPiUKICBnZ3Bsb3QoYWVzKHg9Z3RfdGlzc3VlLCB5ID0gdmFsdWUpKSArCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHRpc3N1ZSkpICsKICAgIGdlb21fbGluZShkYXRhPXNhbXBsZS5laWdlbi5tZWFucywgZ3JvdXAgPSAxLCBsd2Q9LjMpICsgCiAgZmFjZXRfd3JhcCh+TUUsIG5jb2w9NCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0PTEsIHZqdXN0PS41KSkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJTZXQzIikKYGBgCkEgaGVhdCBtYXA6CgpgYGB7ciwgZmlnLmhlaWdodD03fQpNRXMubSA8LSBhcy5tYXRyaXgoTUVzKQpoZWF0bWFwLjIoTUVzLm0sIHRyYWNlPSJub25lIiwgY2V4Um93PSAwLjYsIGNvbD0iYmx1ZXJlZCIpCmBgYAoKCgpgYGB7cn0Kc2F2ZShtb2R1bGUuYXNzaWdubWVudCwgTUVzLCBsY3BtLmZpbHRlciwgQ3JMRlkxLCBDckxGWTIsIGZpbGU9Ii4uL291dHB1dC9XR0NOQV9nYW1ldG9waHl0ZVNhbXBsZXMuUmRhdGEiKQpgYGAKCgo=